package org.erepublik.company.charts;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Set;

import org.erepublik.company.Configuration;
import org.erepublik.company.ECountry;
import org.erepublik.company.EProductType;
import org.erepublik.company.EWorld;
import org.erepublik.company.Location;
import org.erepublik.company.PriceIndex;
import org.erepublik.company.Taxes;
import org.erepublik.company.service.DatabaseProvider;
import org.erepublik.company.service.IVatService;
import org.erepublik.company.thread.ProductUpdater;
import org.erepublik.company.utils.Utils;
import org.json.JSONArray;
import org.json.JSONException;

public abstract class AbstractJSONDataCollector<T extends Object> {


    protected abstract ArrayList<T> getProductFromJSONArray(JSONArray jsonArrayWithFood) throws JSONException;


    
    final int CALCULATED_OFFER_NUM=20;
  
    @SuppressWarnings("unchecked")  
    private ChartData toChartData(ArrayList<T> object, Location country, Enum criteria) {

        T[] bestOffers = getNBestOffers(CALCULATED_OFFER_NUM, object, criteria, country);
        double price = calculateOffers(bestOffers, criteria);
        ChartData chartData = new ChartData();
        chartData.setValue(price);
        return chartData;
    }
    
    
    @SuppressWarnings("unchecked")    
    protected abstract double calculateOffers(T[] bestOffers, Enum criteria);
    @SuppressWarnings("unchecked")
    protected abstract T[] getNBestOffers(int cALCULATEDOFFERNUM, ArrayList<T> object, Enum criteria, Location country);
    
    
    protected HashMap<Location, HashMap<String, ChartDataSet>> data;
    private long lastDataTime = 0;
    private long lastUpdate = 0;
    private DatabaseProvider database = new DatabaseProvider();
    @SuppressWarnings("unchecked")
    private Class<? extends Enum> enumerator;
    

    protected String productName;
    private String productUrl;
    private IVatService taxService;

    @SuppressWarnings("unchecked")
    public AbstractJSONDataCollector(String productName, Class<? extends Enum> class1, IVatService taxService) {
        this.productName = productName;
        this.taxService = taxService;
        this.productUrl = Configuration.databasePath+"/"+productName;
        this.enumerator = class1;
        Location[] countries = Utils.getAllLocations();
        data = new HashMap<Location, HashMap<String, ChartDataSet>>();
        
        HashMap<Location, HashMap<String, ChartDataSet>> chartsFromDatabase = database.getDataSets(productName);
        ChartDataSet someData = chartsFromDatabase.get(EWorld.GLOBAL).get(class1.getEnumConstants()[0].toString());
        
        System.out.println("odczytalem z bazy dane "+productName+" z " +someData.getData().size()+
                " okresow \t ostatni rekord jest z "+ new Date(someData.getLastDataSet()) + " ");
        
        
        for (Location location : countries) {
            HashMap<String, ChartDataSet> map = new HashMap<String, ChartDataSet>();
            data.put(location, map);
            Enum[] foodCriterias = class1.getEnumConstants();
            for (Enum eFoodSearchCriteria : foodCriterias) {
                map.put(eFoodSearchCriteria.toString(), chartsFromDatabase.get(location).get(eFoodSearchCriteria.toString()));
            }
        }

        
        lastDataTime = someData.getLastDataSet();
        lastUpdate = new Date().getTime();
        updateData();
    }

    
    @SuppressWarnings("unchecked")   
    public PriceIndex[] getPricesIndex(Enum criteria, EProductType productType) {
        
        if (lastUpdate < new Date().getTime() - 1000*60*60*1 ) {
            updateData();
            lastUpdate = new Date().getTime();
        }
            
        ArrayList<PriceIndex> priceIndexes = new ArrayList<PriceIndex>();
        Location[] countryList = Utils.getAllLocations();
        HashMap<ECountry, Taxes> taxMap = null;
        try {
            taxMap = taxService.getVat();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        for (Location location : countryList) {
            PriceIndex priceIndex = new PriceIndex();
            priceIndex.setPrice( data.get(location).get(criteria.toString()).getTodayPrice() );
            priceIndex.setPriceYesterday( data.get(location).get(criteria.toString()).getYesterdayPrice() );
            priceIndex.setPriceWeekAgo( data.get(location).get(criteria.toString()).getWeekAgoPrice() );
            priceIndex.setLocation(location);
            try {
                if (location instanceof ECountry ) {
                    priceIndex.setVat( (int) Math.round( taxMap.get(location).getVat(productType)*100)  );
                    priceIndex.setImportTax( (int) Math.round( taxMap.get(location).getImportTax(productType)*100)  );
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            priceIndexes.add(priceIndex);
            
        }
        return priceIndexes.toArray(new PriceIndex[0]);
    }
    
    
    
    
    
    
    public ChartDataSet getData(Location location, String attribute) {
        if (lastUpdate < new Date().getTime() - 1000*60*60*1 ) {
            updateData();
            lastUpdate = new Date().getTime();
        }
        
        return data.get(location).get(attribute);
    }

    private long getLastDataTime() {
        String[] files = getAllJSONFiles();
        return getFileCreationTime(files[files.length-1]);
    }

    @SuppressWarnings("unchecked")
    public void updateData() {
        System.out.println("updating "+productName+" price history service...");
        long newLastDataTime = getLastDataTime();
        if (newLastDataTime <= lastDataTime)
            return;
        String[] files = getNewJSONFiles(lastDataTime);
        HashMap<Location, HashMap<String, ArrayList<ChartData>>> newDataset = getDataFromJSONFiles(files);
        
        
        lastDataTime = newLastDataTime;
        saveDataToDatabase(newDataset);
        for (Location eCountry : Utils.getAllLocations()) {
   
            Enum[] foodCriterias = enumerator.getEnumConstants();
            for (Enum eFoodSearchCriteria : foodCriterias) {
                ArrayList<ChartData> newData = newDataset.get(eCountry).get(eFoodSearchCriteria.toString());
                ChartDataSet oldData = data.get(eCountry).get(eFoodSearchCriteria.toString());
                for (ChartData chartData : newData) {
                    oldData.getData().add(chartData);
                }
 
            }
        }
        

        
    }

    @SuppressWarnings("unchecked")
    private void saveDataToDatabase(HashMap<Location, HashMap<String, ArrayList<ChartData>>> newDataset) {
        Set<Location> set = newDataset.keySet();
        for (Location country : set) {
            Enum[] foodCriterias = enumerator.getEnumConstants();
            for (Enum attribute : foodCriterias) {
                ChartDataSet dataSet = database.getDataSet(country.toString(), attribute.toString(), productName);
                dataSet.setLastDataSet(lastDataTime);
                database.updateObject(dataSet);
                ArrayList<ChartData> data = newDataset.get(country).get(attribute.toString());
                for (ChartData chartData : data) {
                    chartData.setDataSet(dataSet);
                    database.save(chartData);
                }

            }
        }
    }

    @SuppressWarnings("unchecked")
    private HashMap<Location, HashMap<String, ArrayList<ChartData> >> getDataFromJSONFiles(String[] files) {
        HashMap<Location, HashMap<String, ArrayList<ChartData>>> chartsFromDatabase = 
            new HashMap<Location, HashMap<String, ArrayList<ChartData>>>();

        for (Location eCountry : Utils.getAllLocations()) {
            HashMap<String, ArrayList<ChartData>> map = new HashMap<String, ArrayList<ChartData>>();
            chartsFromDatabase.put(eCountry, map);
            Enum[] foodCriterias = enumerator.getEnumConstants();
            for (Enum eFoodSearchCriteria : foodCriterias) {
                map.put(eFoodSearchCriteria.toString(), new ArrayList<ChartData>());
            }
        }

        
        for (String string : files) {
            long time = getFileCreationTime(string);
            System.out.println("getting charts from file "+string);
            
            ArrayList<T> objects = null;
            try {
                String jsonAsString = Utils.convertStreamToString( new FileInputStream(new File(productUrl+"/"+string)) );
                JSONArray jsonArray = new JSONArray(jsonAsString);
                objects = getProductFromJSONArray(jsonArray);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            

            for (Location eCountry : Utils.getAllLocations()) {
                Enum[] foodCriterias = enumerator.getEnumConstants();
                for (Enum eFoodSearchCriteria : foodCriterias) {
                    ChartData data = toChartData(objects, eCountry, eFoodSearchCriteria);
                    data.setTime(time);
                    chartsFromDatabase.get(eCountry).get(eFoodSearchCriteria.toString()).add(data);
                }
            }
        }
        return chartsFromDatabase;
    }


    private String[] getAllJSONFiles() {

        URL urlToDirectory = null;
        try {
            urlToDirectory = new URL("file://" + Configuration.databasePath + "/" + productName);
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
        }

        try {
            File fildirectory = new File(urlToDirectory.toURI());
            File[] files = fildirectory.listFiles();
            ArrayList<String> fileList = new ArrayList<String>();
            for (File file : files) {
                if (file.getPath().endsWith("-" + productName + ".json"))
                    fileList.add(file.getName());
            }
            String[] fileTable = fileList.toArray(new String[0]);
            Arrays.sort(fileTable);
            if (fileTable.length == 0)
                return null;
            return fileTable;
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String[] getNewJSONFiles(long lastDataTime) {

        String[] allFiles = getAllJSONFiles();
        ArrayList<String> files = new ArrayList<String>();
        for (String file : allFiles) {
            long fileTime = getFileCreationTime(file);
            if (fileTime > lastDataTime) {
                files.add(file);
            }
        }
        return files.toArray(new String[0]);
    }

    private long getFileCreationTime(String string) {
        return ProductUpdater.getTimeStampFromFileName( string );
    }

    
    
}
